}
}
+#[cfg(any(feature = "v2018_2", feature = "dox"))]
+#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
+#[derive(Clone, Copy)]
+pub enum RepoCheckoutFilterResult {
+ Allow,
+ Skip,
+ #[doc(hidden)]
+ __Unknown(i32),
+}
+
+#[cfg(any(feature = "v2018_2", feature = "dox"))]
+impl fmt::Display for RepoCheckoutFilterResult {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "RepoCheckoutFilterResult::{}", match *self {
+ RepoCheckoutFilterResult::Allow => "Allow",
+ RepoCheckoutFilterResult::Skip => "Skip",
+ _ => "Unknown",
+ })
+ }
+}
+
+#[cfg(any(feature = "v2018_2", feature = "dox"))]
+#[doc(hidden)]
+impl ToGlib for RepoCheckoutFilterResult {
+ type GlibType = ostree_sys::OstreeRepoCheckoutFilterResult;
+
+ fn to_glib(&self) -> ostree_sys::OstreeRepoCheckoutFilterResult {
+ match *self {
+ RepoCheckoutFilterResult::Allow => ostree_sys::OSTREE_REPO_CHECKOUT_FILTER_ALLOW,
+ RepoCheckoutFilterResult::Skip => ostree_sys::OSTREE_REPO_CHECKOUT_FILTER_SKIP,
+ RepoCheckoutFilterResult::__Unknown(value) => value
+ }
+ }
+}
+
+#[cfg(any(feature = "v2018_2", feature = "dox"))]
+#[doc(hidden)]
+impl FromGlib<ostree_sys::OstreeRepoCheckoutFilterResult> for RepoCheckoutFilterResult {
+ fn from_glib(value: ostree_sys::OstreeRepoCheckoutFilterResult) -> Self {
+ match value {
+ 0 => RepoCheckoutFilterResult::Allow,
+ 1 => RepoCheckoutFilterResult::Skip,
+ value => RepoCheckoutFilterResult::__Unknown(value),
+ }
+ }
+}
+
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Clone, Copy)]
pub enum RepoCheckoutMode {
-use glib::translate::{Stash, ToGlib, ToGlibPtr};
+use glib::translate::{FromGlibPtrNone, Stash, ToGlib, ToGlibPtr};
+use glib_sys::gpointer;
use libc::c_char;
-use ostree_sys::OstreeRepoCheckoutAtOptions;
-use std::path::PathBuf;
+use ostree_sys::{OstreeRepo, OstreeRepoCheckoutAtOptions, OstreeRepoCheckoutFilterResult};
+use std::path::{Path, PathBuf};
+use {Repo, RepoCheckoutFilterResult};
use {RepoCheckoutMode, RepoCheckoutOverwriteMode};
use {RepoDevInoCache, SePolicy};
-#[derive(PartialEq, Eq, Hash, Debug, Clone)]
+pub type RepoCheckoutFilter =
+ Option<Box<dyn Fn(&Repo, &Path, &libc::stat) -> RepoCheckoutFilterResult>>;
+
pub struct RepoCheckoutAtOptions {
pub mode: RepoCheckoutMode,
pub overwrite_mode: RepoCheckoutOverwriteMode,
pub force_copy_zerosized: bool,
pub subpath: Option<PathBuf>,
pub devino_to_csum_cache: Option<RepoDevInoCache>,
- // TODO: those thingamajigs
- // pub filter: OstreeRepoCheckoutFilter,
- // pub filter_user_data: gpointer,
+ // TODO: might be interesting to turn this into a type parameter
+ pub filter: RepoCheckoutFilter,
pub sepolicy: Option<SePolicy>,
pub sepolicy_prefix: Option<String>,
}
force_copy_zerosized: false,
subpath: None,
devino_to_csum_cache: None,
+ filter: None,
sepolicy: None,
sepolicy_prefix: None,
}
type StringStash<'a, T> = Stash<'a, *const c_char, Option<T>>;
+unsafe extern "C" fn filter_trampoline(
+ repo: *mut OstreeRepo,
+ path: *const c_char,
+ stat: *mut libc::stat,
+ user_data: gpointer,
+) -> OstreeRepoCheckoutFilterResult {
+ // TODO: handle unwinding
+ let closure =
+ user_data as *const Box<dyn Fn(&Repo, &Path, &libc::stat) -> RepoCheckoutFilterResult>;
+ let repo = FromGlibPtrNone::from_glib_none(repo);
+ let path: PathBuf = FromGlibPtrNone::from_glib_none(path);
+ let result = (*closure)(&repo, &path, &*stat);
+ result.to_glib()
+}
+
impl<'a> ToGlibPtr<'a, *const OstreeRepoCheckoutAtOptions> for RepoCheckoutAtOptions {
type Storage = (
Box<OstreeRepoCheckoutAtOptions>,
let sepolicy = self.sepolicy.to_glib_none();
options.sepolicy = sepolicy.0;
+ if let Some(filter) = &self.filter {
+ options.filter_user_data = filter
+ as *const Box<dyn Fn(&Repo, &Path, &libc::stat) -> RepoCheckoutFilterResult>
+ as gpointer;
+ options.filter = Some(
+ filter_trampoline
+ as unsafe extern "C" fn(
+ *mut OstreeRepo,
+ *const c_char,
+ *mut libc::stat,
+ gpointer,
+ ) -> OstreeRepoCheckoutFilterResult,
+ );
+ }
+
Stash(options.as_ref(), (options, subpath, sepolicy_prefix))
}
}
mod tests {
use super::*;
use gio::{File, NONE_CANCELLABLE};
- use glib_sys::{GFALSE, GTRUE};
+ use glib_sys::{gpointer, GFALSE, GTRUE};
use ostree_sys::{
OSTREE_REPO_CHECKOUT_MODE_NONE, OSTREE_REPO_CHECKOUT_MODE_USER,
OSTREE_REPO_CHECKOUT_OVERWRITE_NONE, OSTREE_REPO_CHECKOUT_OVERWRITE_UNION_IDENTICAL,
force_copy_zerosized: true,
subpath: Some("sub/path".into()),
devino_to_csum_cache: Some(RepoDevInoCache::new()),
+ filter: Some(Box::new(|_repo, _path, _stat| {
+ RepoCheckoutFilterResult::Skip
+ })),
sepolicy: Some(SePolicy::new(&File::new_for_path("a/b"), NONE_CANCELLABLE).unwrap()),
sepolicy_prefix: Some("prefix".into()),
};
);
assert_eq!((*ptr).unused_ints, [0; 6]);
assert_eq!((*ptr).unused_ptrs, [ptr::null_mut(); 3]);
- assert_eq!((*ptr).filter, None);
- assert_eq!((*ptr).filter_user_data, ptr::null_mut());
+ assert_eq!(
+ (*ptr).filter,
+ Some(
+ filter_trampoline
+ as unsafe extern "C" fn(
+ *mut OstreeRepo,
+ *const c_char,
+ *mut libc::stat,
+ gpointer,
+ )
+ -> OstreeRepoCheckoutFilterResult
+ )
+ );
+ assert_eq!(
+ (*ptr).filter_user_data,
+ options.filter.as_ref().unwrap()
+ as *const Box<dyn Fn(&Repo, &Path, &libc::stat) -> RepoCheckoutFilterResult>
+ as gpointer
+ );
assert_eq!((*ptr).sepolicy, options.sepolicy.to_glib_none().0);
assert_eq!(
CStr::from_ptr((*ptr).sepolicy_prefix),
use gio::NONE_CANCELLABLE;
use glib::prelude::*;
use ostree::{
- ObjectName, ObjectType, RepoCheckoutAtOptions, RepoCheckoutMode, RepoCheckoutOverwriteMode,
- RepoDevInoCache,
+ ObjectName, ObjectType, RepoCheckoutAtOptions, RepoCheckoutFilterResult, RepoCheckoutMode,
+ RepoCheckoutOverwriteMode, RepoDevInoCache,
};
use std::os::unix::io::AsRawFd;
force_copy: true,
force_copy_zerosized: true,
devino_to_csum_cache: Some(RepoDevInoCache::new()),
+ filter: Some(Box::new(|_repo, _path, _stat| {
+ RepoCheckoutFilterResult::Allow
+ })),
..Default::default()
}),
dirfd.as_raw_fd(),
assert_test_file(checkout_dir.path());
}
+
+#[test]
+#[cfg(feature = "v2016_8")]
+fn should_checkout_at_with_filter() {
+ let test_repo = TestRepo::new();
+ let checksum = test_repo.test_commit("test");
+ let checkout_dir = tempfile::tempdir().expect("checkout dir");
+
+ let dirfd = openat::Dir::open(checkout_dir.path()).expect("openat");
+ test_repo
+ .repo
+ .checkout_at(
+ Some(&RepoCheckoutAtOptions {
+ filter: Some(Box::new(|_repo, path, _stat| {
+ if let Some("testfile") = path.file_name().map(|s| s.to_str().unwrap()) {
+ RepoCheckoutFilterResult::Skip
+ } else {
+ RepoCheckoutFilterResult::Allow
+ }
+ })),
+ ..Default::default()
+ }),
+ dirfd.as_raw_fd(),
+ "test-checkout",
+ &checksum,
+ NONE_CANCELLABLE,
+ )
+ .expect("checkout at");
+
+ let testdir = checkout_dir.path().join("test-checkout").join("testdir");
+ assert!(std::fs::read_dir(&testdir).is_ok());
+ assert!(std::fs::File::open(&testdir.join("testfile")).is_err());
+}